﻿using System;
using SCG=System.Collections.Generic;
using Db4C=Db4objects.Db4o.Collections;
using NGenerics.DataStructures.General;
using NGenerics.UI.DataStructures.General;
using SCS=System.Collections.Specialized;

namespace Layers
{

    public interface ICollectionsFactory
    {
        SCG.IList<T> NewList<T>();
        SCG.IList<T> NewList<T>(SCG.IEnumerable<T> elements);
        SCG.IDictionary<K, V> NewDictionary<K, V>();
    }

    public static class Factory 
    {
        internal class NGenerics: ICollectionsFactory
        {
            public SCG.IList<T> NewList<T>()
            {
                return new VisitableList<T>();
            }

            public SCG.IList<T> NewList<T>(SCG.IEnumerable<T> elements)
            {
                return new VisitableList<T>(elements);
            }
            
            public SCG.IDictionary<K, V> NewDictionary<K, V>()
            {
                return new VisitableHashtable<K, V>();
            }
        }
        internal class NGenericsObservable: ICollectionsFactory
        {
            public SCG.IList<T> NewList<T>()
            {
                return new ObservableVisitableList<T>();
            }

            public SCG.IList<T> NewList<T>(SCG.IEnumerable<T> elements)
            {
                return new VisitableList<T>(elements);
            }

            public SCG.IDictionary<K, V> NewDictionary<K, V>()
            {
                return new ObservableVisitableHashtable<K, V>();
            }
        }
        internal class Db4o : ICollectionsFactory
        {
            public SCG.IList<T> NewList<T>()
            {
                return new Db4C.ArrayList4<T>();
            }

            public SCG.IList<T> NewList<T>(SCG.IEnumerable<T> elements)
            {
                return new Db4C.ArrayList4<T>(elements as SCG.ICollection<T>);
            }

            public SCG.IDictionary<K, V> NewDictionary<K, V>()
            {
                return new ObservableVisitableHashtable<K, V>();
            }
        }

        private static NGenerics _fast = new NGenerics();
        private static NGenericsObservable _observable = new NGenericsObservable();
        private static Db4o _db4 = new Db4o();

        private static ICollectionsFactory _current = new NGenerics();



        #region basic collections 

        public static SCG.IList<T> NewList<T>()
        {
            return _current.NewList<T>();
        }


        public static SCG.IDictionary<K, V> NewDictionary<K, V>()
        {
            return _current.NewDictionary<K, V>();
        }

        public static SCG.IList<T> NewList<T>(SCG.IEnumerable<T> elements)
        {
            return _current.NewList<T>(elements);
        }

        public static IBag<T> NewBag<T>()
        {
            return new Bag<T>();
        }

        public static SCG.ICollection<T> NewLinkedList<T>()
        {
            return new SCG.LinkedList<T>();
        }

        public static SCG.IList<T> NewSortedList<T>(SCG.IComparer<T> comparer)
        {
            return new SortedList<T>(comparer);
        }

        #endregion

        #region observable collections
        public static ObservableVisitableList<T> NewObsList<T>()
        {
            return new ObservableVisitableList<T>();
        }

        public static ObservableVisitableList<T> NewObsList<T>(SCG.IEnumerable<T> elements)
        {
            return new ObservableVisitableList<T>(elements);
        }

        public static ObservableVisitableHashtable<K, V> NewObsDictionary<K, V>()
        {
            return new ObservableVisitableHashtable<K, V>();
        }

        public static ObservableBag<T> NewObsBag<T>()
        {
            return new ObservableBag<T>();
        }

        public static SCG.IList<T> ObservableFor<T>(SCG.IList<T> src)
        {
            if (src is SCS.INotifyCollectionChanged)
                return src;
            return new ObservableListWrapper<T>(src);
        }
        #endregion
    }
    
    public delegate void Function<T>(T element);

    public static partial class ObservableExtensions
    {
        public static SCG.IList<T> WithNotify<T>(this SCG.IList<T> coll, SCS.NotifyCollectionChangedEventHandler h)
        {
            SCS.INotifyCollectionChanged ncc = coll as SCS.INotifyCollectionChanged;
            if (null != ncc)
                ncc.CollectionChanged += h;
            return coll;
        }

        public static SCG.IList<T> WithAdd<T>(this SCG.IList<T> coll, Function<T> f) where T:class
        {
            SCS.INotifyCollectionChanged ncc = coll as SCS.INotifyCollectionChanged;
            if (null != ncc)
                ncc.CollectionChanged += delegate(object source, SCS.NotifyCollectionChangedEventArgs args)
                {
                    if (args.Action == SCS.NotifyCollectionChangedAction.Add)
                        foreach (object obj in args.NewItems)
                            f(obj as T);
                };
            return coll;
        }

        public static SCG.IList<T> WithRemove<T>(this SCG.IList<T> coll, Function<T> f) where T : class
        {
            SCS.INotifyCollectionChanged ncc = coll as SCS.INotifyCollectionChanged;
            if (null != ncc)
                ncc.CollectionChanged += delegate(object source, SCS.NotifyCollectionChangedEventArgs args)
                {
                    if (args.Action == SCS.NotifyCollectionChangedAction.Remove)
                        foreach (object obj in args.OldItems)
                            f(obj as T);
                };
            return coll;
        }
    }

    public static partial class ObservableDictionaryExtensions
    {
        public static SCG.IDictionary<K, V> WithNotify<K, V>(this SCG.IDictionary<K, V> coll, SCS.NotifyCollectionChangedEventHandler h)
        {
            SCS.INotifyCollectionChanged ncc = coll as SCS.INotifyCollectionChanged;
            if (null != ncc)
                ncc.CollectionChanged += h;
            return coll;
        }
    }

    public interface IPersistent
    {
        void Activate();
    }

}